// SDL2_13 [OpenGL ES Cube 7].nova // Interactive OpenGL ES colour cube. // By Robert Platt. // Using namespace declarations. using library.emscripten; using library.math; using library.opengl; using library.sdl2; class OpenGL_Quad { public float b; public float t; public float l; public float r; } // The application class. class SDL2_13_OpenGL_ES_Cube_7 { // Static data members. private static SDL_Window window; private static SDL_Renderer r; private static SDL_GLContext c; private static int initialSizeX; private static int initialSizeY; private static int sizeX; private static int sizeY; private static int Pmatrix; private static int Vmatrix; private static int Mmatrix; private static uint index_buffer; private static float[] proj_matrix; private static float[] mov_matrix; private static float[] view_matrix; private static int mouseX, mouseY; private static double spinX, spinY; private static bool mouseButtonDown; private static bool spinPaused; private static bool done; private static bool fullScreenMode; private static SDL_Area prevWindowSize; // Used for the desktop app full-screen mode. private static bool emscriptenActive; // Application class's "main" function. public static void main( String[] args ) { Stream.writeLine( "SDL2_13 [OpenGL ES Cube 7].nova" ); // Output the contols. Stream.write( "\nControls:\n" + " drag pointer / swipe screen = spin cube\n" + " left click / tap screen = stop spinning\n" + " space = pause / resume spinning\n" + " f = toggle full screen mode\n" + " esc = exit full screen mode\n\n" ); // Initialise the class's data members. mouseX = 0; mouseY = 0; spinX = 0; spinY = 0; mouseButtonDown = false; spinPaused = false; done = false; fullScreenMode = false; initialSizeX = 640; initialSizeY = 480; sizeX = initialSizeX; sizeY = initialSizeY; emscriptenActive = Emscripten.isActive( ); // Disable the resize control. Emscripten.disableResizeControl( ); // Show the cursor in full screen mode. Emscripten.setHideMousePointerCheckbox( false ); // Set the full screen mode callback. Emscripten.setFullScreenChangeCallback( "SDL2_13_OpenGL_ES_Cube_7", fullScreenModeChange ); // Setup SDL. SDL2.SDL_Init( SDL2.SDL_INIT_VIDEO ); // Setup SDL for OpenGL. SDL2.SDL_GL_SetAttribute( SDL2.SDL_GL_CONTEXT_MAJOR_VERSION, 2 ); SDL2.SDL_GL_SetAttribute( SDL2.SDL_GL_CONTEXT_MINOR_VERSION, 0 ); SDL2.SDL_GL_SetAttribute( SDL2.SDL_GL_DOUBLEBUFFER, 1 ); SDL2.SDL_GL_SetAttribute( SDL2.SDL_GL_DEPTH_SIZE, 24 ); // Create the SDL2 window. window = SDL2.SDL_CreateWindow( "SDL2_13_OpenGL_ES_Cube_7", SDL2.SDL_WINDOWPOS_CENTERED, SDL2.SDL_WINDOWPOS_CENTERED, sizeX, sizeY, SDL2.SDL_WINDOW_OPENGL | SDL2.SDL_WINDOW_RESIZABLE ); // Check for a null reference. if ( window == null ) { // Output an error message. Stream.writeLine( "Failed to create window: " + SDL2.SDL_GetError( ) ); // Abort the application. return; } r = SDL2.SDL_CreateRenderer( window, -1, SDL2.SDL_RENDERER_ACCELERATED | SDL2.SDL_RENDERER_PRESENTVSYNC ); c = SDL2.SDL_GL_CreateContext( window ); // Set the background colour. OpenGL.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); /* float[] vertices = { -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1, // Back face. -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1, // Front face. -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1, // Left face. 1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1, // Right face. -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1, // Bottom face. -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1 // Top face. }; float[] colors = { 1,0,0, 0,0,0, 0,0,1, 1,0,1, // Back face: bottom right = red, bottom left = black, top left = blue, top right = magenta. 1,1,0, 0,1,0, 0,1,1, 1,1,1, // Front face: bottom left = yellow, bottom right = green, top right = cyan, top left = white. 1,0,0, 1,0,1, 1,1,1, 1,1,0, // left face: bottom left = red, top left = magenta, top right = white, bottom right = yellow. 0,0,0, 0,0,1, 0,1,1, 0,1,0, // Right face: bottom right = black, top right = blue, top left = cyan, bottom left = green. 1,0,0, 1,1,0, 0,1,0, 0,0,0, // Bottom face: bottom left = red, top left = yellow, top right = green, bottom right = black. 1,0,1, 1,1,1, 0,1,1, 0,0,1 // Top face: top left = magenta, bottom left = white, bottom right = cyan, top right = blue. }; sbyte[] indices = { 0,1,2, 0,2,3, // Rear face. 4,5,6, 4,6,7, // Front face. 0,3,7, 0,7,4, // Left face. 1,2,6, 1,6,5, // Right face. 0,4,5, 0,5,1, // Bottom face. 3,7,6, 3,6,2 // Top face. };*/ // The cube has eight vertices. float[] vertices = { -1,-1,-1, // Lower back left. 1,-1,-1, // Lower back right. 1, 1,-1, // Upper back right. -1, 1,-1, // Upper back left. -1,-1, 1, // Lower front left. 1,-1, 1, // Lower front right. 1, 1, 1, // Upper front right. -1, 1, 1 // Upper front left. }; // Each of the above vertices has a corresponding colour. // NOTE: Interpolation was already enabled in the previous cube example, // but it wasn't visible because the cube's faces had uniform colours. float[] colors = { 1,0,0, // Red. 0,0,0, // Black. 0,0,1, // Blue. 1,0,1, // Magenta. 1,1,0, // Yellow. 0,1,0, // Green. 0,1,1, // Cyan. 1,1,1 // White. }; // We can specify each triangle using its indices. // NOTE: Each cube face (quad) is made up of two triangles. sbyte[] indices = { 0,1,2, 0,2,3, // Rear face (red, black, blue magenta). 4,5,6, 4,6,7, // Front face (yellow, green, cyan, white). 0,3,7, 0,7,4, // Left face (red, magenta, white, yellow). 1,2,6, 1,6,5, // Right face (black, blue, cyan, green). 0,4,5, 0,5,1, // Bottom face (red, yellow, green, black). 3,7,6, 3,6,2 // Top face (magenta, white, cyan, blue). }; uint[] vbos = new uint[ 3 ]; OpenGL.glGenBuffers( 3, vbos ); // Create and store data into vertex buffer uint vertex_buffer = vbos[ 0 ]; OpenGL.glBindBuffer( OpenGL.GL_ARRAY_BUFFER, vertex_buffer ); OpenGL.glBufferData( OpenGL.GL_ARRAY_BUFFER, vertices, OpenGL.GL_STATIC_DRAW ); // Create and store data into color buffer uint color_buffer = vbos[ 1 ]; OpenGL.glBindBuffer( OpenGL.GL_ARRAY_BUFFER, color_buffer ); OpenGL.glBufferData( OpenGL.GL_ARRAY_BUFFER, colors, OpenGL.GL_STATIC_DRAW ); // Create and store data into index buffer index_buffer = vbos[ 2 ]; OpenGL.glBindBuffer( OpenGL.GL_ELEMENT_ARRAY_BUFFER, index_buffer ); OpenGL.glBufferData( OpenGL.GL_ELEMENT_ARRAY_BUFFER, indices, OpenGL.GL_STATIC_DRAW ); // The shaders. String vertCode = "attribute vec3 position; \n" + "uniform mat4 Pmatrix; \n" + "uniform mat4 Vmatrix; \n" + "uniform mat4 Mmatrix; \n" + "attribute vec3 color; \n" + // The colour of the vertex. "varying vec3 vColor; \n" + " \n" + "void main( void ) \n" + // pre-built function "{ \n" + " gl_Position = Pmatrix * Vmatrix * Mmatrix * vec4( position, 1.0 ); \n" + " vColor = color; \n" + "} \n"; String fragCode = "precision mediump float; \n" + "varying vec3 vColor; \n" + " \n" + "void main( void ) \n" + "{ \n" + " gl_FragColor = vec4( vColor, 1.0 ); \n" + "} \n"; uint vertShader = OpenGL.glCreateShader( OpenGL.GL_VERTEX_SHADER ); OpenGL.glShaderSource( vertShader, vertCode ); OpenGL.glCompileShader( vertShader ); uint fragShader = OpenGL.glCreateShader( OpenGL.GL_FRAGMENT_SHADER ); OpenGL.glShaderSource( fragShader, fragCode ); OpenGL.glCompileShader( fragShader ); uint shaderProgram = OpenGL.glCreateProgram( ); OpenGL.glAttachShader( shaderProgram, vertShader ); OpenGL.glAttachShader( shaderProgram, fragShader ); OpenGL.glLinkProgram( shaderProgram ); OpenGL.glUseProgram( shaderProgram ); // Position. OpenGL.glBindBuffer( OpenGL.GL_ARRAY_BUFFER, vertex_buffer ); int position = OpenGL.glGetAttribLocation( shaderProgram, "position" ); OpenGL.glVertexAttribPointer( (uint)position, 3, OpenGL.GL_FLOAT, false, 0, 0 ) ; OpenGL.glEnableVertexAttribArray( (uint)position ); // Colour. OpenGL.glBindBuffer( OpenGL.GL_ARRAY_BUFFER, color_buffer ); int color = OpenGL.glGetAttribLocation( shaderProgram, "color" ); OpenGL.glVertexAttribPointer( (uint)color, 3, OpenGL.GL_FLOAT, false, 0, 0 ) ; OpenGL.glEnableVertexAttribArray( (uint)color ); // Get the uniform locations. Pmatrix = OpenGL.glGetUniformLocation( shaderProgram, "Pmatrix" ); Vmatrix = OpenGL.glGetUniformLocation( shaderProgram, "Vmatrix" ); Mmatrix = OpenGL.glGetUniformLocation( shaderProgram, "Mmatrix" ); calculateProjectionMatrix( ); mov_matrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; view_matrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; // Translating z. view_matrix[ 14 ] -= 5; // zoom. // Orientate the cube. rotateY( mov_matrix, 45.0f ); rotateX( mov_matrix, 30.0f ); // Check for the emscripten environment. if ( emscriptenActive ) { int simulate_infinite_loop = 1; // Call the function repeatedly. int fps = -1; // Call the function as fast as the browser wants to render (typically 60fps). Emscripten.setMainLoop( renderFrame, fps, simulate_infinite_loop ); } else { // Rendering and event processing loop. do { renderFrame( ); } while( !done ); } // Shutdown app. OpenGL.glDeleteProgram( shaderProgram ); OpenGL.glDeleteShader( vertShader ); OpenGL.glDeleteShader( fragShader ); OpenGL.glDeleteBuffers( 3, vbos ); SDL2.SDL_GL_DeleteContext( c ); SDL2.SDL_DestroyRenderer( r ); SDL2.SDL_DestroyWindow( window ); SDL2.SDL_Quit( ); } // Full screen mode callback. public static bool fullScreenModeChange( int eventType, EmscriptenFullScreenChangeEvent e, Object userObject ) { /* Stream.writeLine( "fullScreenModeChange - called" ); Stream.writeLine( "eventType = " + Integer.toString( eventType ) ); Stream.writeLine( "isFullscreen = " + Boolean.toString( e.isFullScreen ) ); Stream.writeLine( "fullscreenEnabled = " + Boolean.toString( e.fullScreenEnabled ) ); Stream.writeLine( "nodeName = " + e.nodeName ); Stream.writeLine( "id = " + e.id ); Stream.writeLine( "userObject = " + (String)userObject );*/ // Check for full screen mode. if ( e.isFullScreen ) { // Update the size of the window. sizeX = e.elementWidth; sizeY = e.elementHeight; } else { // Update the size of the window. sizeX = initialSizeX; sizeY = initialSizeY; } // Record the state. fullScreenMode = e.isFullScreen; // Set the canvas size. Emscripten.setCanvasSize( sizeX, sizeY ); // Set the window size. SDL2.SDL_SetWindowSize( window, sizeX, sizeY ); // Recalculate the projection matrix. calculateProjectionMatrix( ); // Return 'true' for handling the event. return true; } public static void calculateProjectionMatrix( ) { // Calculate the projection matrix. // float *proj_matrix = get_projection( 30, sizeX / sizeY, 1, 100 ); // float angle = 30.0f, aspectRatio = sizeX / sizeX, zMin = 1.0f, zMax = 100.0f; // float ang = Math.tan( ( angle * 0.5f ) * (float)Math.PI / 180.0f ); // angle * 0.5. /* proj_matrix = { 0.5f / ang, 0 , 0, 0, 0, 0.5f * aspectRatio / ang, 0, 0, 0, 0, -( zMax + zMin ) / ( zMax - zMin ), -1, 0, 0, ( -2.0f * zMax * zMin ) / ( zMax - zMin ), 0 };*/ float fovyInDegrees = 45.0f; float aspectRatio = (float)sizeX / (float)sizeY; float near = 1.0f; float far = 100.0f; proj_matrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; // setProjectionMatrix( 30.0f, 1.0f, 100.0f, proj_matrix ); OpenGL_Quad q = gluPerspective( fovyInDegrees, aspectRatio, near, far ); glFrustum( q, near, far, proj_matrix ); } public static void setProjectionMatrix( float angleOfView, float near, float far, float[] m ) { // Set the basic projection matrix. float scale = 1.0f / Math.tan( angleOfView * 0.5f * (float)Math.PI / 180.0f ); m[ 0 ] = scale; // Scale the x coordinates of the projected point. m[ 5 ] = scale; // Scale the y coordinates of the projected point. m[ 10 ] = -far / (far - near); // Used to remap z to [ 0, 1 ]. m[ 11 ] = -1.0f; // Set w = -z. m[ 14 ] = -far * near / (far - near); // Used to remap z [ 0, 1 ]. m[ 15 ] = 0.0f; } // Compute screen coordinates. public static OpenGL_Quad gluPerspective( float fovY, float aspect, float zNear, float zFar ) { float scale = Math.tan( fovY * 0.5f * (float)Math.PI / 180.0f ) * zNear; OpenGL_Quad q = new OpenGL_Quad( ); q.r = aspect * scale; q.l = -q.r; q.t = scale; q.b = -q.t; return q; } // Set the OpenGL perspective projection matrix. public static void glFrustum( OpenGL_Quad q, float n, float f, float[] m ) { // Set OpenGL perspective projection matrix. m[ 0 ] = 2 * n / ( q.r - q.l ); m[ 1 ] = 0; m[ 2 ] = 0; m[ 3 ] = 0; m[ 4 ] = 0; m[ 5 ] = 2 * n / ( q.t - q.b ); m[ 6 ] = 0; m[ 7 ] = 0; m[ 8 ] = ( q.r + q.l ) / ( q.r - q.l ); m[ 9 ] = ( q.t + q.b ) / ( q.t - q.b ); m[ 10 ] = -( f + n ) / ( f - n ); m[ 11 ] = -1; m[ 12 ] = 0; m[ 13 ] = 0; m[ 14 ] = -2 * f * n / ( f - n ); m[ 15 ] = 0; } public static void onWindowEvent( SDL_WindowEvent e ) { // Stream.writeLine( "SDL_WINDOWEVENT" ); // Check for a non null reference after the object cast. if ( e != null ) { switch ( e.event ) { case SDL2.SDL_WINDOWEVENT_RESIZED : { // Stream.writeLine( "SDL_WINDOWEVENT_RESIZED" ); // Update the size of the window. sizeX = e.data1; sizeY = e.data2; // Recalculate the projection matrix. calculateProjectionMatrix( ); break; } } } } public static void onMouseButtonDown( SDL_MouseButtonEvent e ) { // Stream.writeLine( "SDL_MOUSEBUTTONDOWN" ); // Check for a non null reference after the object cast. if ( e != null ) { // byte b = e.button; // Stream.writeLine( Byte.toString( b ) ); if ( e.button == SDL2.SDL_BUTTON_LEFT ) { // Stream.writeLine( "Left button pressed." ); // Update the state of the left mouse button. mouseButtonDown = true; // Stop the cube from spinning. spinX = spinY = 0; } } } public static void onMouseButtonUp( SDL_MouseButtonEvent e ) { // Stream.writeLine( "SDL_MOUSEBUTTONUP" ); // Check for a non null reference after the object cast. if ( e != null ) { // Byte b = new Byte( e.button ); // Stream.writeLine( b.toString( ) ); if ( e.button == SDL2.SDL_BUTTON_LEFT ) { // Stream.writeLine( "Left button released." ); // Update the state of the left mouse button. mouseButtonDown = false; } } } public static void onMouseMotion( SDL_MouseMotionEvent e ) { // Stream.writeLine( "SDL_MOUSEMOTION" ); // Check for a non null reference after the object cast. if ( e != null ) { int x = e.x; int y = e.y; // Stream.writeLine( "x = " + Integer.toString( x ) + " y = " + Integer.toString( y ) ); if ( ( e.state & SDL2.SDL_BUTTON_LMASK ) != 0 ) { // Stream.writeLine( "Left button pressed (mouse motion)." ); // SDL_Log( "Mouse Button 1 (left) is pressed." ); spinX = (double)( y - mouseY ) / 2.0; spinY = (double)( x - mouseX ) / 2.0; // Orientate the cube. rotateCube( mov_matrix ); mouseButtonDown = true; spinPaused = false; // Resume spinning. } mouseX = x; mouseY = y; } } public static void renderFrame( ) { ///////////////////////// // SDL event handling. // ///////////////////////// // Try to get an SDL event. SDL_Event e = SDL2.SDL_PollEvent( ); // Check for a non null reference. if ( e != null ) { // Switch on the type of the event. switch ( e.id ) { case SDL2.SDL_WINDOWEVENT : { onWindowEvent( (SDL_WindowEvent)e ); break; } case SDL2.SDL_MOUSEBUTTONDOWN : { onMouseButtonDown( (SDL_MouseButtonEvent)e ); break; } case SDL2.SDL_MOUSEBUTTONUP : { onMouseButtonUp( (SDL_MouseButtonEvent)e ); break; } case SDL2.SDL_MOUSEMOTION : { onMouseMotion( (SDL_MouseMotionEvent)e ); break; } case SDL2.SDL_KEYDOWN : { // Cast to a keyboard event. SDL_KeyboardEvent kbEvent = (SDL_KeyboardEvent)e; // Switch on the event key. switch ( kbEvent.sym ) { case SDL2.SDLK_f : { // Stream.writeLine( "'f' key pressed." ); // Toggle full screen mode. toggleFullScreenMode( ); break; } case SDL2.SDLK_SPACE : { // Stream.writeLine( "'SPACE' key pressed." ); // Pause / resume spinning. spinPaused = !spinPaused; break; } case SDL2.SDLK_ESCAPE : { // Stream.writeLine( "'ESC' key pressed." ); // Exit full screen mode for the desktop app. if ( !emscriptenActive && fullScreenMode ) toggleFullScreenMode( ); break; } } break; } case SDL2.SDL_QUIT : { // Stream.writeLine( "SDL_QUIT" ); done = true; break; } } } //////////////////// // Spin the cube. // //////////////////// // Check if the mouse button is up and spinning not paused. if ( ( mouseButtonDown == false ) && ( spinPaused == false ) ) { // Spin the cube. rotateCube( mov_matrix ); } ////////////////////////////////////////////////// // Validate the canvas size in emscripten mode. // ////////////////////////////////////////////////// // Check for emscripten mode. if ( emscriptenActive ) { // Get the dimensions of the canvas. int canvasWidth = Emscripten.getCanvasWidth( ); int canvasHeight = Emscripten.getCanvasHeight( ); // Abort drawing this frame if we have a zero width or zero height canvas. // NOTE: This can occur when switching to full-screen mode. if ( ( canvasWidth == 0 ) || ( canvasHeight == 0 ) ) { return; } // Check the canvas size before drawing (diagnostic code). if ( ( canvasWidth != sizeX ) || ( canvasHeight != sizeY ) ) { Stream.writeLine( "CANVAS SIZE MISMATCH DETECTED" ); Stream.writeLine( "canvasWidth" + Integer.toString( canvasWidth ) ); Stream.writeLine( "canvasHeight" + Integer.toString( canvasHeight ) ); Stream.writeLine( "sizeX" + Integer.toString( sizeX ) ); Stream.writeLine( "sizeY" + Integer.toString( sizeY ) ); } } /////////////////////////////// // Render the current frame. // /////////////////////////////// OpenGL.glEnable( OpenGL.GL_DEPTH_TEST ); OpenGL.glDepthFunc( OpenGL.GL_LESS ); OpenGL.glClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); // Grey colour. OpenGL.glClearDepth( 1.0 ); OpenGL.glViewport( 0, 0, sizeX, sizeY ); OpenGL.glClear( OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT ); OpenGL.glUniformMatrix4fv( Pmatrix, 1, false, proj_matrix ); OpenGL.glUniformMatrix4fv( Vmatrix, 1, false, view_matrix ); OpenGL.glUniformMatrix4fv( Mmatrix, 1, false, mov_matrix ); OpenGL.glBindBuffer( OpenGL.GL_ELEMENT_ARRAY_BUFFER, index_buffer ); OpenGL.glDrawElements( OpenGL.GL_TRIANGLES, 36, OpenGL.GL_UNSIGNED_BYTE ); SDL2.SDL_GL_SwapWindow( window ); } // Toggle full screen mode. public static void toggleFullScreenMode( ) { // Check for emscripten mode. if ( emscriptenActive ) { // Check the full screen mode flag. if ( fullScreenMode ) { // Exit full screen mode. Emscripten.exitFullScreenMode( ); } else { // Enter full screen mode. Emscripten.enterFullScreenMode( ); } } else // Nova SDL2 desktop app. { // Get the current mode. fullScreenMode = ( SDL2.SDL_GetWindowFlags( window ) & SDL2.SDL_WINDOW_FULLSCREEN ) != 0; // Are we in windowed mode? if ( !fullScreenMode ) { // Record the size of the window before switching to full screen mode. prevWindowSize = SDL2.SDL_GetWindowSize( window ); } // Toggle the full screen mode flag. fullScreenMode = !fullScreenMode; // Update the SDL2 full screen mode. SDL2.SDL_SetWindowFullscreen( window, fullScreenMode? SDL2.SDL_WINDOW_FULLSCREEN : 0 ); // Show the cursor. // SDL2.SDL_ShowCursor( (int)SDL2.SDL_ENABLE ); // Check for full screen mode. if ( fullScreenMode ) { // Get the display index. int displayIndex = SDL2.SDL_GetWindowDisplayIndex( window ); // Get the size of the whole desktop (the display resolution). SDL_DisplayMode dm = SDL2.SDL_GetDesktopDisplayMode( displayIndex ); // Check for a null reference. if ( dm == null ) { // Output an error message. Stream.write( "Error getting desktop display mode\n" ); // Abort the operation. return; } // Set the size to the current display. sizeX = dm.w; sizeY = dm.h; } else { // Revert to the previous window size. sizeX = prevWindowSize.w; sizeY = prevWindowSize.h; } // Set the window size. SDL2.SDL_SetWindowSize( window, sizeX, sizeY ); // Recalculate the projection matrix. calculateProjectionMatrix( ); } } // Rotate the cube. public static void rotateCube( float[] mov_matrix ) { // Rotate the rotation matrix. rotateY( mov_matrix, (float)spinY ); rotateX( mov_matrix, (float)spinX ); } public static void rotateZ( float[] m, float angle ) { angle *= (float)( Math.PI / 180.0 ); // Convert to radians. float c = Math.cos( angle ); float s = Math.sin( angle ); float mv0 = m[ 0 ], mv4 = m[ 4 ], mv8 = m[ 8 ]; m[ 0 ] = c * m[ 0 ] - s * m[ 1 ]; m[ 4 ] = c * m[ 4 ] - s * m[ 5 ]; m[ 8 ] = c * m[ 8 ] - s * m[ 9 ]; m[ 1 ] = c * m[ 1 ] + s * mv0; m[ 5 ] = c * m[ 5 ] + s * mv4; m[ 9 ] = c * m[ 9 ] + s * mv8; } public static void rotateX( float[] m, float angle ) { angle *= (float)( Math.PI / 180.0 ); // Convert to radians. float c = Math.cos( angle ); float s = Math.sin( angle ); float mv1 = m[ 1 ], mv5 = m[ 5 ], mv9 = m[ 9 ]; m[ 1 ] = m[ 1 ] * c - m[ 2 ] * s; m[ 5 ] = m[ 5 ] * c - m[ 6 ] * s; m[ 9 ] = m[ 9 ] * c - m[ 10 ] * s; m[ 2 ] = m[ 2 ] * c + mv1 * s; m[ 6 ] = m[ 6 ] * c + mv5 * s; m[ 10 ] = m[ 10 ] * c + mv9 * s; } public static void rotateY( float[] m, float angle ) { angle *= (float)( Math.PI / 180.0 ); // Convert to radians. float c = Math.cos( angle ); float s = Math.sin( angle ); float mv0 = m[ 0 ], mv4 = m[ 4 ], mv8 = m[ 8 ]; m[ 0 ] = c * m[ 0 ] + s * m[ 2 ]; m[ 4 ] = c * m[ 4 ] + s * m[ 6 ]; m[ 8 ] = c * m[ 8 ] + s * m[ 10 ]; m[ 2 ] = c * m[ 2 ] - s * mv0; m[ 6 ] = c * m[ 6 ] - s * mv4; m[ 10 ] = c * m[ 10 ] - s * mv8; } }